import React, { useEffect, useMemo, useState } from "react";
import { BrowserRouter, HashRouter, MemoryRouter, useNavigate } from "react-router-dom";
import { I18nProvider } from "./I18nProvider";
import { Provider, useService } from "../../Service/Provider";
import { Application } from "../Core/Application";
import { useAppContext, AppContext } from "./Provider";
import { useMount } from "../../hook/useMount";
import * as Validation from "../../Validation";
import lodash from "lodash";


interface IAppProps {
    router: "Hash"|"Browser"|"Memory";
    basename?: string;
    ServiceConfig?: React.ComponentType<any>;
    children?: any;
    i18n: any;
}

const AppWrapper = ({ children, ...others }: any) => {
    const navigateTo = useNavigate();
    const appContext = useAppContext();
    const service = useService();
    const [ application ] = useState(() => new Application({
        navigateTo,
        appContext,
        service,
        entry: appContext.config.entry
    }));
    const [ deviceType, setDeviceType ] = useState(application.deviceType);
    const nextAppContext = useMemo(() => ({
        ...appContext,
        deviceType,
        application
    }), [appContext, application, deviceType]);

    const renderChildren = useMemo(() => {
        const childList = lodash.isArray(children) ? children : [children];
        return childList.map((item, index) => {
            if(React.isValidElement(item)) {
                return React.cloneElement(item as any, {
                    ...others,
                    key: index
                });
            }
            return item;
        });
    }, [children, others]);
    useMount(() => {
        application.on("onDeviceTypeChange", (deviceType) => {
            setDeviceType(deviceType)
        });
    });
    return (<AppContext.Provider value={nextAppContext}>{renderChildren}</AppContext.Provider>);
};

export const App = (props: IAppProps) => {
    const { ServiceConfig, router, children, ...others } = props;
    
    const [ Router ] = useState(() => {
        switch (router) {
            case "Hash":
                return HashRouter;
            case "Browser":
                return BrowserRouter;
            case "Memory":
                return MemoryRouter;
            default:
                return HashRouter;
        }
    });

    return  (<I18nProvider messages={props.i18n}>
        <Provider>
            <Validation.Provider>
                <Router basename={props.basename}>
                    <AppWrapper {...others}>
                        { ServiceConfig && <ServiceConfig>{children}</ServiceConfig>}
                        { !ServiceConfig && <>{children}</> }
                    </AppWrapper>
                </Router>
            </Validation.Provider>
        </Provider>
    </I18nProvider>);
};